package spring.parsing;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Element;
import org.dom4j.Node;
import spring.aop.*;
import spring.exceptions.BeanInitializationException;
import spring.factory.SingletonBeanRegistry;
import spring.resources.DomElementHolder;
import spring.transaction.PlatformTransactionManager;

import java.util.List;
import java.util.Objects;

public class XmlTxParser extends AbstractXmlParser {

    public XmlTxParser(SingletonBeanRegistry singletonBeanRegistry) {
        super(singletonBeanRegistry);
    }

    private TxAdviceRegistry getTxAdviceRegistry() {
        return (TxAdviceRegistry) singletonBeanRegistry;
    }

    @Override
    public XmlNodeType getType() {
        return XmlNodeType.TX;
    }

    @Override
    public void parse(Element element) {
        String id = element.attributeValue("id");
        if (StringUtils.isBlank(id)) {
            throw new BeanInitializationException("事务切面配置需要给定ID，以进行事务绑定，请修改配置文件。");
        }
        if (singletonBeanRegistry.containsSingleton(id)) {
            return;
        }
        String transactionManager = element.attributeValue("transaction-manager");
        if (StringUtils.isBlank(transactionManager)) {
            transactionManager = "transactionManager";
        }
        if (!singletonBeanRegistry.containsSingleton(transactionManager)) {
            XmlParser beanParser = XmlParserFactory.getInstance().create(XmlNodeType.BEAN, singletonBeanRegistry);
            Element transactionElement = DomElementHolder.getElement(transactionManager);
            if (Objects.isNull(transactionElement)) {
                throw new BeanInitializationException("当前事务管理器节点没有正确配置，请检查配置文件。");
            }
            beanParser.parse(transactionElement);
        }
        Advice advice = new Advice();
        advice.setId(id);
        PlatformTransactionManager ptm = (PlatformTransactionManager) singletonBeanRegistry.getSingleton(transactionManager);
        advice.setTransaction(ptm.getTransaction());
        AopConfig aopConfig = getTxAdviceRegistry().getAopConfig();
        if (aopConfig.containsFactory(id)) {
            AopPointcut pointcut = aopConfig.getAopPointcut(id);
            aopConfig.addAdvisor(advice, pointcut);
        }
        List<Node> nodes = element.selectNodes("//tx-method");
        for (Node node : nodes) {
            Element methodEl = (Element) node;
            AdviceMethod adviceMethod = new AdviceMethod();
            adviceMethod.setName(methodEl.attributeValue("name"));
            adviceMethod.setPropagation(methodEl.attributeValue("propagation"));
            adviceMethod.setReadOnly(methodEl.attributeValue("read-only"));
            adviceMethod.setRollbackFor(methodEl.attributeValue("rollback-for"));
            advice.addMethod(adviceMethod);
        }
        getTxAdviceRegistry().registerAdvice(id, advice);

    }

}
